home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Language/OS - Multiplatform Resource Library
/
LANGUAGE OS.iso
/
t3_1
/
doc.lha
/
documentation
/
manual
/
environment.ms
< prev
next >
Wrap
Text File
|
1987-06-30
|
14KB
|
391 lines
@part[ENVIRONMENT, root "TMAN.MSS"] @Comment{-*-System:TMAN-*-}
@chap[Environments]
@label[EnvironmentsChapter] @Comment{ref: semantics chapter}
@section[Environments and contours]
@label[environments]
@iix[Environments] are associations between @iix[identifiers] (variable
names) and @iix[values]. One such association between an identifier
and a value is called a @iixs[binding]. In general, environments
are created implicitly, for example, on entering @tc[LAMBDA]-bodies.
@index[variables]
@dc{ However, environments may be obtained as objects via the @tc[LOCALE]
special form; see section @ref[LocaleSection]. }
@dc{ -- Explain the term @i[variable]. }
Environments are organized hierarchically into @iix[contours]. Each
contour augments an @qu"outer" environment, providing bindings for a few
identifiers. These bindings are in effect in the body of the
expression which introduces the contour. The portion of a program in which
a binding is in effect is known as the variable's @iix[scope].
For example:
@begin[ProgramExample]
(BLOCK
(LIST 'A 'B)
...
(LAMBDA (A B C)
... ; [1]
(LAMBDA (D E)
... ; [2]
)
... ; [3]
(LAMBDA (A F G)
... ; [4]
)
...)
...)
@end[ProgramExample]
Each @tc[LAMBDA] introduces a new contour. This code presumably occurs in
an outermost environment in which the variable @tc[LIST] is bound.
(The values of @tc[BLOCK] and @tc[LAMBDA] are not in question here;
they are reserved words, and their bindings as variables are irrelevant.)
At point @tc<[1]> in the program, a new environment is in effect
which now has bindings for @tc[A], @tc[B], and @tc[C], as well as for
any variables known in the outer environment. At @tc<[2]>,
@tc[D] and @tc[E] also have values. @tc<[3]> is in the
scope of @tc[A], @tc[B], and @tc[C] but not in the scope of
@tc[D] and @tc[E]. Finally, the outer binding
of any given identifier is @i[shadowed]@index[shadowing]
by inner bindings of that identifier;
an occurrence of the identifier @tc[A]
appearing at @tc<[4]> refers not to the outer @tc[A]
but to the inner one, because the inner one shadows the outer.
There are two kinds of contours, known as @i[lambda-contours] and
@i[locales]. Lambda-contours are introduced by many special forms,
such as @tc[LAMBDA], @tc[LET], and @tc[LABELS]. Locales are
introduced by @tc[LOCALE] special forms.
@section[Local variables]
@info[NOTES="Special form"]
@desc[@el[](LET @i[specs] . @i[body]) @yl[] @i[value-of-body]]
@tc[LET] provides a convenient syntax for introducing local bindings.
Each @i[spec] should be a two-element list of the form @tc[(@i[variable
value])]. The @i[value] expressions are all evaluated (in no particular
order), then the
@i[body] (an implicit block) is evaluated in an environment where the
@i[variables] are bound to those values. The value of @i[body] is
yielded.
@begin[ProgramExample]
(LET ((X 2)) X) @ev[] 2
(LET ((@i[var@-(1) val@-(1)]) (@i[var@-(2) val@-(2)]) ... (@i[var@-(n) val@-(n)])) . @i[body])
@ce[] ((LAMBDA (@i[var@-(1) var@-(2) ... var@-(n)]) . @i[body]) @i[val@-(1) val@-(2) ... val@-(n)])
@end[ProgramExample]
See also @tc[DESTRUCTURE] (page @PageRef[destructure]).
@EndDesc[LET]
@info[NOTES="Special form"]
@desc[(LET* @i[specs] . @i[body]) @yl[] @i[value-of-body]]
@tc[LET*] is like @tc[LET], except that the binding of variables to
values is done sequentially, so that the second binding
is done in an environment in which the first binding has already occurred,
etc. In a @tc[LET]-expression, all the bindings are done in one
environment.
For example,
suppose the variable @tc[A] is already bound to @tc[10] when
the expression @wt[(LET ((A 20) (B A)) B)] is evaluated. The
result is @tc[10], not @tc[20], because @tc[B] is bound to
@tc[A]'s value in the outer environment. In contrast,
@wt[(LET* ((A 20) (B A)) B)] evaluates to @tc[20].
@begin[ProgramExample]
(LET* ((@i[var@-[1] val@-[1]]) (@i[var@-[2] val@-[2]]) ... (@i[var@-[n] val@-[n]])) . @i[body])
@ce[]
(LET ((@i[var@-[1] val@-[1]]))
(LET ((@i[var@-[2] val@-[2]]))
...
(LET ((@i[var@-[n] val@-[n]])) . @i[body])...))
@ce[]
((LAMBDA (@i[var@-[1]])
((LAMBDA (@I[var@-[2]])
...
((LAMBDA (@I[var@-[n]]) . @i[body])
@I[val@-[n]]) ...)
@i[val@-[2]]))
@i[val@-[1]])
@end[ProgramExample]
@EndDesc[LET*]
@AnEquivE[Tfn="LABELS",Efn="LABEL"]
@info[NOTES="Special form"]
@desc[@el[](LABELS @i[specs] . @i[body]) @yl[] @i[value-of-body]]
@tc[LABELS] is useful for defining local procedures that may be
mutually recursive.
Each @i[spec] should be of the form
@wt[(@i[variable value])].
The @i[value]-expressions are evaluated (in no particular order), and
the @i[body] is evaluated in an environment
where all the @i[variables] are bound to those values.
However, @tc[LABELS] differs from @tc[LET]
in that
the bindings of the @i[variables] are already in effect (scope) before the
@i[value]-expressions are evaluated, so that the
@i[value]-expressions can refer to any of the @i[variables],
including the ones to which they themselves will be bound (thus permitting
local recursive procedures).
Each @i[spec] may alternatively have the form
@begin[ProgramExample]
@wt[((@i[variable] . @i[argument-vars]) . @i[body])].
@end[ProgramExample]
This is equivalent to
@begin[ProgramExample]
@wt[(@i[variable] (LAMBDA @i[argument-vars] . @i[body]))]
@end[ProgramExample]
This is intended to parallel the syntax for @tc[DEFINE] (see page
@PageRef(define)).
In the following example, note that @tc[REV-1] is bound to a value that
refers to @tc[REV-1].
@begin[ProgramExample]
@tabclear
(DEFINE (REVERSE L)
(LABELS (((REV-1 L RESULT-SO-FAR)
(COND ((NULL? L) RESULT-SO-FAR)
(ELSE
(REV-1 (CDR L)
(CONS (CAR L) RESULT-SO-FAR))))))
(REV-1 L '())))
@tabclear
@end[ProgramExample]
As an extension to the dialect of SCHEME described in
@cite[STEELE78REV], @tau[] does not restrict the @i[value] expressions
to be @tc[LAMBDA]-expressions.
However, the values of the @i[variables]
are not defined until @i[body] is evaluated; i.e., they should
not be used in the @i[value]-expressions. For example,
@teg[(LABELS ((A B) (B 1)) . @i[body])]
is ill-formed;
at the point when the expressions @tc[B] and @tc[1] are being evaluated,
the variable @tc[B] is bound,
but will have an undefined value. Consequently, @tc[A] will have an undefined
value. By contrast, in the well-formed expression
@begin[teg]
(LABELS ((A (LAMBDA (X) (+ X B))) (B 1)) . @i[body])@r[,]
@end[teg]
which can also be written
@begin[teg]
(LABELS (((A X) (+ X B)) (B 1)) . @i[body])@r[,]
@end[teg]
the value of @tc[B] is not used before @i[body] is evaluated.
The @tc[LAMBDA]-expression evaluates
to a @i[closure] that includes @tc[B]. By the time @tc[A] (the closure)
is called, @tc[B] will have a value.
@tc[LABELS] cannot easily be described in terms of @tc[LAMBDA].
However, it can be described in terms of @tc[LAMBDA] and @tc[SET].
(@tc[SET] performs side-effects on bindings; see page @pageref[SET].)
This is misleading because @tc[LABELS] should not be thought of as a
side-effecting operator. @dc{?}
@begin[TEG]
(LABELS ((@i[var@-[1]] @i[val@-[1]])
(@i[var@-[2]] @i[val@-[2]])
...
(@i[var@-[n]] @i[val@-[n]]))
. @i[body])
@ce[]
(LET ((@i[var@-[1]] (UNDEFINED-VALUE))
(@i[var@-[2]] (UNDEFINED-VALUE))
...
(@i[var@-[n]] (UNDEFINED-VALUE)))
(SET @i[var@-[1]] @i[val@-[1]])
(SET @i[var@-[2]] @i[val@-[2]])
...
(SET @i[var@-[n]] @i[val@-[n]])
. @i[body])
@end[TEG]
This equivalence is overly concrete in that the order of evaluation of
the @i[val@-[i]] expressions, and the sequence in which the
variables are given their values, is not defined.
@EndDesc[LABELS]
@section[Locales]
@label[LocaleSection] @Comment{ref: above; intro to env chapter}
@i[Locales] serve two purposes. First, they provide an incremental or
declarative syntax for creating variable bindings in a contour. Second,
they provide access to environments as objects which can be dynamically
manipulated. The term @iixs[locale] is used to mean both the kind of
contour introduced by a @tc[LOCALE]-expression, and the object which
represents the environment.
There are no global variables in @Tau[]; all variables are lexically
bound in some contour. Locales play the role of what is known in other
Lisp and Scheme dialects as the global environment.
@info[NOTES="Special form"]
@desc[(LOCALE @i[variable . body]) @yl[] @i[value-of-body]]
Introduces a locale and evaluates @i[body] (an implicit block) in it.
The value of the @tc[LOCALE] expression is that of @i[body]'s last form.
Within @i[body], @i[variable] is bound to the locale.
If @i[variable] is @tc[()], then the locale is not accessible as an object.
@BeginInset[Bugs:]
@Timp[] 2.7 implements @tc[LOCALE] incorrectly in several different
ways. First, locales are created permanently; once created, a
locale's storage is never reclaimed, even if it is completely
inaccessible. This means that for now they should only be used
to create environments statically, not, for example, in a loop,
or whenever some procedure is called.
Second, forward references to shadowed variables don't work.
For example:
@begin[ProgramExample]
(LET ((A 10)) (LOCALE () (DEFINE (F) A) (DEFINE A 20) (F)))
@end[ProgramExample]
yields @tc[10] instead of @tc[20].
Third, TC doesn't handle them very well; it just lets @tc[EVAL] interpret
the body of the locale. For all these reasons, it is preferable
to use @tc[MAKE-LOCALE] (page @pageref[MAKE-LOCALE]) instead
of @tc[LOCALE] wherever possible.
@EndInset[]
@EndDesc[LOCALE]
@AnEquivE[Tfn="DEFINE",Efn="DE"]
@AnEquivE[Tfn="DEFINE",Efn="DEFVAR"]
@info[NOTES="Special form",EQUIV="DEFUN"]
@descN[
F1="@el[](DEFINE @i[variable] @i[value]) @yl[] @i[undefined]",
FN1="DEFINE",
F2="@el[](DEFINE (@i[variable] . @i[arguments]) . @i[body]) @yl[] @i[undefined]"
]
@tc[DEFINE] creates a binding for @i[variable]
in the lexically innermost locale in which the @tc[DEFINE] expression occurs.
The variable is given @i[value] as its value.
The second @tc[DEFINE] syntax is for defining procedures.
@begin[TEG]
(DEFINE (@i[variable] . @i[arguments]) . @i[body])
@end[TEG]
is equivalent to
@begin[TEG]
(DEFINE @i[variable] (LAMBDA @i[arguments] . @i[body]))
@end[TEG]
For example:
@begin[ProgramExample]
(DEFINE (F X) (LIST X 2)) @ce[] (DEFINE F (LAMBDA (X) (LIST X 2)))
(DEFINE (F . X) (LIST X 2)) @ce[] (DEFINE F (LAMBDA X (LIST X 2)))
@end[ProgramExample]
@EndDescN[]
@info[NOTES="Special form"]
@Desc[(LSET @i[variable value]) @yl[] @i[value]]
@tc[LSET] is identical to @tc[DEFINE] except that the procedure
definition syntax is not permitted, and the variable is declared to be
alterable. Variables bound by @tc[DEFINE] may not be altered
with @tc[SET]; variables bound by @tc[LSET] may be.
See @tc[SET], page @pageref[SET].
@EndDesc[LSET]
Any @tc[LOCALE]-expression whose @i[variable] position is @tc[()]
may be rewritten as an expression
involving only @tc[LET] or @tc[LABELS]. In this case the use of
@tc[LOCALE] is simply a notational difference; using @tc[LOCALE] instead
of @tc[LET] or @tc[LABELS] may make code easier or harder to read and
manipulate.
@begin[TEG]
(LOCALE ()
(DEFINE FOO ...)
(DEFINE BAR ...)
...)
@ce[]
(LABELS ((FOO ...)
(BAR ...))
...)
@end[TEG]
This transformation may not be possible if the @tc[LOCALE]-expression
binds a variable to the locale; in this case new bindings may be added
as a result of calls to @tc[*DEFINE] or @tc[*LSET] (see below), and
there is no way to anticipate what variables should be bound by the
@tc[LABELS] or @tc[LET] expression.
References in a @tc[LOCALE] body to variables bound later
on in that locale are undefined. For example,
@begin[TEG]
(LET ((A 10)) (LOCALE () (LET ((B A)) (DEFINE A 20) B)))
@end[TEG]
has an undefined effect; it may yield @tc[10] or @tc[20],
or it may signal an error.
@BeginInset[Rationale:]
The undefinedness of forward references permits implementations
flexibility in dealing with the declarative nature of @tc[DEFINE]
and @tc[LSET]. New bindings may be registered either when
encountered during evaluation, to simplify evaluation, or may
be registered during a pre-processing (compilation) phase, to
permit compilation to a more efficient representation.
@EndInset[]
@desc[(MAKE-LOCALE @i[superior-locale] @i[identification]) @yl[] @i[locale]]
Creates a locale inferior to @i[superior-locale]. @i[Identification]
may be any object, and is used only for debugging purposes.
@begin[TEG]
(DEFINE *FOO-ENV* (MAKE-LOCALE *STANDARD-ENV* '*FOO-ENV*))
@end[TEG]
@enddesc[MAKE-LOCALE]
@desc[(MAKE-EMPTY-LOCALE @i[identification]) @yl[] @i[locale]]
Creates a locale containing no bindings whatsoever.
@enddesc[MAKE-EMPTY-LOCALE]
@info[NOTES="Type predicate"]
@desc[(LOCALE? @i[object]) @yl[] @i[boolean]]
Returns true if @i[object] is a locale.
@enddesc[LOCALE?]
@section[Non-local reference]
@info[notes="Settable"]
@desc[(*VALUE @i[locale identifier]) @yl[] @i[object]]
Accesses the value of @i[identifier] in @i[locale].
If @i[identifier] has no value in @i[locale], then
the effect of the call to @tc[*VALUE] is undefined.
@begin[ProgramExample]
(*VALUE *STANDARD-ENV* 'T) @ev[] @r[true]
(*VALUE (LOCALE Z (DEFINE A 10) Z) 'A) @ev[] 10
@end[ProgramExample]
@EndDesc[*VALUE]
@desc[(*DEFINE @i[locale] @i[identifier] @i[value]) @yl[] @i[undefined]]
Defines the value of @i[identifier] in @i[locale] to be @i[value].
@enddesc[*DEFINE]
@desc[(*LSET @i[locale] @i[identifier] @i[value]) @yl[] @i[value]]
Creates a binding for @i[identifier] in @i[locale] with initial
value @i[value].
@enddesc[*LSET]
@desc[(IMPORT @i[locale] . @i[variables]) @yl[] @i[undefined]]
Locally defines @i[variables] to have the same values as their values
in @i[locale].
@begin[ProgramExample]
(IMPORT @i[locale] @i[var@-[1] var@-[2] ... var@-[n]])
@ce[]
(BLOCK (DEFINE @i[var@-[1]] (*VALUE @i[locale] '@i[var@-[1]]))
(DEFINE @i[var@-[2]] (*VALUE @i[locale] '@i[var@-[2]]))
...
(DEFINE @i[var@-[n]] (*VALUE @i[locale] '@i[var@-[n]])))
@end[ProgramExample]
@enddesc[IMPORT]